package cqrtplm.log;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

/**
 * 通用对象比较工具类
 */
public class ObjectComparator {

    private static final ObjectMapper objectMapper = new ObjectMapper();


    @Target({ElementType.TYPE, ElementType.FIELD}) // 只能用在字段上
    @Retention(RetentionPolicy.RUNTIME) // 运行时可通过反射获取
    public @interface RTComment {
        String value(); // 注解值：字段的中文注释名
    }

    /**
     * 标记不需要比较的字段
     */
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface RTIgnore {
    }

    /**
     * 比较两个对象的差异（支持新旧对象任意一个为null的情况）
     */
    public static <T> CompareResult compare(T oldObj, T newObj, String modifier) {
        // 校验对象合法性
        validateObjects(oldObj, newObj);
        Class<?> clazz = oldObj != null ? oldObj.getClass() : newObj.getClass();

        // 构建比较结果
        CompareResult result = new CompareResult();
        result.setClassName(clazz.getSimpleName());
        result.setClassNameComment(getClassComment(clazz));
        result.setModifier(modifier);
        result.setModifyTime(new Timestamp(System.currentTimeMillis()));

        List<Difference> differences = compareFields(oldObj, newObj, clazz);
        result.setDetails(differences);
        result.setNoChange(differences.isEmpty()); // 标记是否有变更

        return result;
    }

    /**
     * 获取类的注释（从RTComment注解）
     */
    private static String getClassComment(Class<?> clazz) {
        RTComment annotation = clazz.getAnnotation(RTComment.class);
        return annotation != null ? annotation.value() : clazz.getSimpleName();
    }

    /**
     * 比较对象的所有字段差异
     */
    private static <T> List<Difference> compareFields(T oldObj, T newObj, Class<?> clazz) {
        List<Difference> details = new ArrayList<>();
        List<Field> allFields = getAllFields(clazz);

        for (Field field : allFields) {
            // 跳过标记了RTIgnore的字段
            if (field.isAnnotationPresent(RTIgnore.class)) {
                continue;
            }

            try {
                field.setAccessible(true);
                Object oldValue = oldObj != null ? field.get(oldObj) : null;
                Object newValue = newObj != null ? field.get(newObj) : null;

                if (!isValueEqual(oldValue, newValue)) {
                    Difference diff = new Difference();
                    diff.setF(field.getName());
                    diff.setC(getFieldComment(field));
                    diff.setO(oldValue != null ? oldValue.toString() : "null");
                    diff.setN(newValue != null ? newValue.toString() : "null");
                    details.add(diff);
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException("比较字段失败：" + field.getName(), e);
            }
        }
        return details;
    }

    private static <T> void validateObjects(T oldObj, T newObj) {
        if (oldObj == null && newObj == null) {
            throw new IllegalArgumentException("两个比较对象不能同时为null");
        }
        if (oldObj != null && newObj != null && !oldObj.getClass().equals(newObj.getClass())) {
            throw new IllegalArgumentException("对象类型不一致，无法比较");
        }
    }

    private static List<Field> getAllFields(Class<?> clazz) {
        List<Field> fields = new ArrayList<>();
        while (clazz != null && clazz != Object.class) {
            for (Field field : clazz.getDeclaredFields()) {
                fields.add(field);
            }
            clazz = clazz.getSuperclass();
        }
        return fields;
    }

    private static String getFieldComment(Field field) {
        RTComment annotation = field.getAnnotation(RTComment.class);
        return annotation != null ? annotation.value() : field.getName();
    }

    private static boolean isValueEqual(Object oldValue, Object newValue) {
        if (oldValue == null && newValue == null) return true;
        if (oldValue == null || newValue == null) return false;
        return oldValue.equals(newValue);
    }

    /**
     * 比较结果主类（包含类信息、修改信息和差异详情）
     */
    public static class CompareResult {
        private String className;
        private String classNameComment;
        private String modifier;
        private Timestamp modifyTime;
        private List<Difference> details;
        private boolean noChange; // 新增字段，标识是否有变更

        public String getDetailsAsJson() {
            try {
                return objectMapper.writeValueAsString(details);
            } catch (JsonProcessingException e) {
                throw new RuntimeException("差异详情转换JSON失败", e);
            }
        }

        // getter和setter
        public String getClassName() { return className; }
        public void setClassName(String className) { this.className = className; }
        public String getClassNameComment() { return classNameComment; }
        public void setClassNameComment(String classNameComment) { this.classNameComment = classNameComment; }
        public String getModifier() { return modifier; }
        public void setModifier(String modifier) { this.modifier = modifier; }
        public Timestamp getModifyTime() { return modifyTime; }
        public void setModifyTime(Timestamp modifyTime) { this.modifyTime = modifyTime; }
        public List<Difference> getDetails() { return details; }
        public void setDetails(List<Difference> details) { this.details = details; }
        public boolean isNoChange() { return noChange; }
        public void setNoChange(boolean noChange) { this.noChange = noChange; }
    }

    /**
     * 差异详情类（单个字段的差异）
     */
    public static class Difference {
        private String F; // 字段名
        private String C; // 字段注释
        private String O; // 旧值
        private String N; // 新值

        // getter和setter
        public String getF() { return F; }
        public void setF(String f) { F = f; }
        public String getC() { return C; }
        public void setC(String c) { C = c; }
        public String getO() { return O; }
        public void setO(String o) { O = o; }
        public String getN() { return N; }
        public void setN(String n) { N = n; }
    }
}